package fons.navigator.ui.browsers.dimensions;

import java.awt.geom.Point2D;
import java.util.Collection;

import org.jbox2d.dynamics.joints.DistanceJoint;
import org.jbox2d.dynamics.joints.DistanceJointDef;

import edu.umd.cs.piccolo.nodes.PPath;
import edu.umd.cs.piccolo.util.PBounds;
import fons.navigator.util.box2d.Box2D;
import fons.navigator.util.piccolo.ContextMenu;
import fons.navigator.util.piccolo.ContextMenuOwner;
import fons.navigator.util.piccolo.PLabel;

public class Connection extends PPath implements ContextMenuOwner {
	private static final long serialVersionUID = 1L;

	private final Block source, target;
	private PLabel label;
	private final DimensionBrowserCanvas canvas;

	private DistanceJoint joint;
	private boolean flexible;
	private final ContextMenu menu;

	protected Connection (DimensionBrowserCanvas canvas, Block a, Block b, boolean flexible) {
		this.source = a;
		this.target = b;
		this.canvas = canvas;
		this.flexible = flexible;
		this.menu = new ContextMenu(canvas.getCamera());
	}

	public void setLabel (PLabel label) {
		if (this.label != null) {
			removeChild(this.label);
		}

		this.label = label;
		addChild(label);
	}

	public Block getSource() {
		return source;
	}

	public Block getTarget() {
		return target;
	}

	public Block getOpposite (Block a) {
		if (a == this.source) return this.target;
		else if (a == this.target) return this.source;
		return null;
	}

	public boolean connects (Block a) {
		return this.source == a || this.target == a;
	}

	public boolean isFlexible () {
		return flexible;
	}

	public void updatePosition () {
		this.reset();

		final PBounds aBounds = source.getGlobalFullBounds();
		final PBounds bBounds = target.getGlobalFullBounds();

		moveTo((float) aBounds.getCenterX(), (float) aBounds.getCenterY());
		lineTo((float) bBounds.getCenterX(), (float) bBounds.getCenterY());
		
		

		if (label != null) {
			updateLabel();
		}
	}

	private void updateLabel () {
		if (label != null) {
			final double x = getFullBoundsReference().getCenterX();
			final double y = getFullBoundsReference().getCenterY();
			label.setOffset(x, y);
		}
	}

	private void createJoint () {
		if (this.isFlexible()) return;

		final DistanceJointDef jointDef = new DistanceJointDef();

		jointDef.initialize(source.getBody(), target.getBody(), source.getBody().getWorldCenter(), target.getBody().getWorldCenter());
		jointDef.collideConnected = true;

		final PBounds aBounds = source.getGlobalFullBounds();
		final PBounds bBounds = target.getGlobalFullBounds();

		Point2D ap = aBounds.getCenter2D();
		Point2D bp = bBounds.getCenter2D();

		jointDef.length = Box2D.toBox2D(ap.distance(bp));
		jointDef.frequencyHz = 2.0f;
		jointDef.dampingRatio = 1.0f;
		joint = (DistanceJoint) canvas.getWorld().createJoint(jointDef);
	}

	private void removeJoint () {
		if (joint != null) {
			canvas.getWorld().destroyJoint(joint);
			joint = null;
		}
	}

	public void rejoin () {
		if (!(source.isFixated() && target.isFixated())) {
			createJoint();
		}
	}

	public void disjoin () {
		removeJoint();
	}

	public void setFlexible (boolean flexible) {
		this.flexible = flexible;
		disjoin();
		rejoin();
	}

	public static void rejoinAll (Collection<Connection> connections) {
		for (Connection c: connections) {
			c.rejoin();
		}
	}

	public static void disjoinAll (Collection<Connection> connections) {
		for (Connection c: connections) {
			c.disjoin();
		}
	}

	@Override
	public ContextMenu getMenu() {
		return menu;
	}
}
